/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.util.*;
import Util.*;
import java.util.ArrayList;

/**
 *
 * @author u61851
 */
public abstract class MaqAutomata {

    private ArrayList<Moneda> monedas; // las monedas que estan habilitadas para el uso de las maquinas
    private ArrayList<Billete> billetes; // los billetes que estan habilitados para el uso de las maquinas
    private ArrayList<Cinta> cAuditoria;
    private Fondo fondo;
    private Monto monto;
    private Cambio cambio;
    private static String pwdAdmin = "maquinaAutomata";

    public MaqAutomata(ArrayList<Moneda> monedas, ArrayList<Billete> billetes) {
        this.monedas = monedas;
        this.billetes = billetes;
        this.cAuditoria = new ArrayList<Cinta>();
    }

    public ArrayList<Moneda> getMonedas() {
        return monedas;
    }

    public ArrayList<Billete> getBilletes() {
        return billetes;
    }

    public Cambio getCambio() {
        return cambio;
    }

    public void setCambio(Cambio cambio) {
        this.cambio = cambio;

        Hashtable<Dinero, Integer> ht = this.getCambio().getValores();
        this.restarAlFondo(ht);
    }

    public Fondo getFondo() {
        return fondo;
    }

    public void setFondo(Fondo fondo) {
        this.fondo = fondo;
    }

    public Monto getMonto() {
        return monto;
    }

    public void setMonto(Monto monto) { // tiene que agregar al monto y ademas al fondo.. esas monedas/billetes los puedo usar para el cambio despues
        this.monto = monto;

        Hashtable<Dinero, Integer> ht = monto.getValores();
        this.sumarAlFondo(ht);
    }

    public ArrayList<Cinta> getcAuditoria() {
        return this.cAuditoria;
    }

    public void addcAuditoria(Cinta c) {
        this.cAuditoria.add(c);
    }

    public void setPwdAdmin(String pwdNueva) {
        MaqAutomata.pwdAdmin = pwdNueva;
    }

    public static String getPwdAdmin() {
        return MaqAutomata.pwdAdmin;
    }

    public void sumarAlFondo(Hashtable<Dinero, Integer> suma) {
        Hashtable<Dinero, Integer> htFondo = this.getFondo().getValores();
        int cant;

        if (suma != null && suma.size() > 0) {
            for (Enumeration e = suma.keys(); e.hasMoreElements();) {
                Dinero d = (Dinero) e.nextElement();

                if (htFondo.containsKey(d)) { // si existe este dinero en suma, lo tengo que sumar a fondo
                    cant = (htFondo.get(d) + suma.get(d)); // sumo la cantidad de ese elemento a la cantidad de fonod
                    htFondo.remove(d); // remuevo de fondo
                    htFondo.put(d, cant); // lo vuelvo a poner con la cantidad corregida
                } else {
                    htFondo.put(d, suma.get(d));
                }
            }
            Fondo f = new Fondo(htFondo);
            this.setFondo(f);
        }
    }

    public void restarAlFondo(Hashtable<Dinero, Integer> resta) {
        Hashtable<Dinero, Integer> htFondo = this.getFondo().getValores();
        int cant;

        if (resta != null && resta.size() > 0) {
            for (Enumeration e = resta.keys(); e.hasMoreElements();) {
                Dinero d = (Dinero) e.nextElement();

                if (htFondo.containsKey(d)) { // si existe este dinero en resta, lo tengo que restar a fondo
                    cant = ((htFondo.get(d) > resta.get(d)) ? (htFondo.get(d) - resta.get(d)) : 0); // resto la cantidad de ese elemento a la cantidad de fonod
                    htFondo.remove(d); // remuevo de fondo
                    htFondo.put(d, cant); // lo vuelvo a poner con la cantidad corregida
                } else {
                    htFondo.put(d, resta.get(d));
                }
            }
            Fondo f = new Fondo(htFondo);
            this.setFondo(f);
            this.getCambio();
        }
    }

    public abstract void informeEstadoMaquina();

    public abstract void iniciarSimulacion();

    /**
     * Muestra las Monedas habilitadas
     */
    public void mostrarMonedas() {
        for (Moneda m : this.getMonedas()) {
            System.out.println("Moneda ( Cod. " + m.getCodigo() + " ) de valor: $" + m.getValor());
        }
    }

    /**
     * Muestra los Billetes habilitados
     */
    public void mostrarBilletes() {
        for (Billete b : this.getBilletes()) {
            System.out.println("Moneda ( Cod. " + b.getCodigo() + " ) de valor: $" + b.getValor());
        }
    }

    /**
     * Informa las Monedas disponibles en el Fondo
     */
    public void mostrarMonedasFondo() {
        ArrayList<Moneda> monedas = new ArrayList<Moneda>();
        Hashtable<Dinero, Integer> f = new Hashtable<Dinero, Integer>(); // este fondo siempre tiene monedas
        f = this.getFondo().getValores();

        for (Enumeration e = f.keys(); e.hasMoreElements();) {
            monedas.add((Moneda) e.nextElement());
        }

        for (Moneda m : monedas) {
            System.out.println("Moneda ( Cod. " + m.getCodigo() + " ) de valor: $" + m.getValor() + " con una cantidad de " + f.get(m));
        }
    }

    /**
     * Informa del Dinero disponible en el Cmabio, tanto Billetes como Monedas
     */
    public void mostrarCambio() {

        Hashtable<Dinero, Integer> f = new Hashtable<Dinero, Integer>(); // este cambio siempre tiene monedas
        f = this.getCambio().getValores();

        for (Enumeration e = f.keys(); e.hasMoreElements();) {
            Dinero din = (Dinero) e.nextElement();
            System.out.println("Valor: $" + din.getValor() + " Cantidad: " + f.get(din));
        }
    }

    /**
     * Informa del Dinero disponible en el Monto, tanto Billetes como Monedas
     */
    public void mostrarMonto() {

        Hashtable<Dinero, Integer> m = new Hashtable<Dinero, Integer>(); // este cambio siempre tiene monedas
        m = this.getMonto().getValores();

        for (Enumeration e = m.keys(); e.hasMoreElements();) {
            Dinero din = (Dinero) e.nextElement();
            System.out.println("Valor: $" + din.getValor() + " Cantidad: " + m.get(din));
        }
    }

    /**
     * Informa sobre los Billetes disponibles en el Fondo
     */
    public void mostrarBilletesFondo() {
        ArrayList<Billete> billetes = new ArrayList<Billete>();
        Hashtable<Dinero, Integer> f = this.getFondo().getValores();

        Dinero d;

        for (Enumeration e = f.keys(); e.hasMoreElements();) {
            d = (Dinero) e.nextElement();

            if (d.sosMoneda() == false) // me aseguro que sea billetes, porque este fondo tiene monedas y billetes
            {
                billetes.add((Billete) d);
            }
        }

        for (Billete b : billetes) {
            System.out.println("Billete ( Cod. " + b.getCodigo() + " ) de valor: $" + b.getValor());
        }
    }

    /**
     * Obtiene la Moneda a partir del codigo
     * @param codigo Integer
     * @return Moneda
     */
    public Moneda getMoneda(int codigo) {
        ArrayList<Moneda> almonedas = new ArrayList<Moneda>();
        almonedas = this.getMonedas(); // obtengo todas las monedas habilitadas

        Moneda m;
        int i = 0;

        while (i < almonedas.size() && almonedas.get(i).getCodigo() != codigo) {
            i++;
        }
        if (i < almonedas.size()) {
            m = almonedas.get(i);
        } else {
            m = null;
        }
        return m;
    }

    /**
     * Obtiene el Billete a partir del codigo
     * @param codigo Integer
     * @return Billete
     */
    public Billete getBillete(int codigo) {
        ArrayList<Billete> albilletes = new ArrayList<Billete>();
        albilletes = this.getBilletes(); // obtengo todas las monedas habilitadas

        Billete b;
        int i = 0;
        while (i < albilletes.size() && albilletes.get(i).getCodigo() != codigo) {
            i++;
        }
        if (i < albilletes.size()) {
            b = albilletes.get(i);
        } else {
            b = null;
        }
        return b;
    }

    /**
     * Agrega monedas al Fondo
     */
    public void agregarMonedasAFondo() {
        int cantActual = 0;
        int cod, cod2, stock;
        char f;
        Fondo fondo;
        Scanner in = new Scanner(System.in);
        Hashtable<Dinero, Integer> valores;
        if (this.getFondo().getValores().size() > 0) {
            valores = this.getFondo().getValores();
        } else {
            valores = new Hashtable<Dinero, Integer>();
        }

        do {
            System.out.println("Ingrese el codigo de moneda que va a ingresar: ");
            this.mostrarMonedas(); // muestro las monedas habilitadas
            cod = in.nextInt();

            Moneda m = this.getMoneda(cod); // consigo la moneda que quiero agregar al fondo
            if (m != null && !valores.containsKey(m)) { // si encuentro la moneda pero no esta en el fondo
                System.out.print("Ingrese la cantidad de monedas: ");
                stock = in.nextInt(); // ingresa la cantidad y lo agrega al ht de valores
                valores.put(m, stock);
            } else if (m != null) { // la moneda esta habilitada, y tambien existe en el fondo
                do {
                    System.out.print("La moneda que intenta agregar al fondo ya existe. Si desea sumar monedas, presione 1, de lo contrario 0.");
                    cod2 = in.nextInt();
                    if (cod2 == 1) {
                        System.out.print("Ingrese la cantidad de monedas que desea agregar: ");
                        stock = in.nextInt();
                        cantActual = (valores.get(m) + stock);
                        valores.remove(m);
                        valores.put(m, cantActual);
                    }
                } while (cod2 == 1);
            } else {
                System.out.println("La moneda que intenta agregar no existe");
            }

            System.out.print("\nSi desea agregar otra moneda presione 'y' O 'Y', sino 'n' O 'N' para salir.");
            f = in.next().charAt(0);
        } while ((f == 'y' || f == 'Y') && f != 'n' && f != 'N'); // me aseguro que haya monedas

        fondo = new Fondo(valores);
        this.setFondo(fondo); // seteo el fondo nuevo
    }
}
